package org.cybzacg.convert.converters.date;

import lombok.extern.slf4j.Slf4j;
import org.cybzacg.convert.annotation.Converter;
import org.cybzacg.convert.constants.DateFormats;
import org.cybzacg.convert.context.ConversionContext;
import org.cybzacg.convert.core.AbstractTypeConverter;
import org.cybzacg.convert.enums.ConversionError;
import org.cybzacg.convert.enums.ConversionPriority;
import org.cybzacg.convert.enums.ConversionType;
import org.cybzacg.convert.exception.ConversionException;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.util.Date;

/**
 * 通用时间类型转换器
 * 作为兜底转换器处理各种时间类型之间的转换
 * 当其他专门的时间转换器无法处理时，使用此转换器
 *
 * @author cybzacg.blog
 * @version 4.0
 * @since 3.0
 */
@Slf4j
@Converter(name = "DateTimeConverter", priority = ConversionPriority.MEDIUM_HIGH, description = "通用时间类型转换器，处理各种时间类型之间的转换", version = "4.0", author = "cybzacg.blog", supportedTypes = {
        TemporalAccessor.class, Date.class, LocalDate.class, LocalTime.class,
        LocalDateTime.class, ZonedDateTime.class, Instant.class, Year.class,
        YearMonth.class, Period.class, Duration.class }, tags = { "datetime", "time", "conversion" })
public class DateTimeConverter extends AbstractTypeConverter {

    private static final ZoneId DEFAULT_ZONE_ID = ZoneId.systemDefault();

    public DateTimeConverter() {
        super(ConversionPriority.MEDIUM_HIGH);
        log.debug("DateTimeConverter 初始化完成，优先级: {}", ConversionPriority.MEDIUM_HIGH);
    }

    @Override
    protected ConversionType getConversionType() {
        return ConversionType.DATE;
    }

    @Override
    @SuppressWarnings("unchecked")
    protected <T> T doConvert(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {

        log.debug("开始时间类型转换: 源类型={}, 目标类型={}, 默认值={}",
                source != null ? source.getClass().getSimpleName() : "null",
                targetType.getSimpleName(),
                defaultValue);

        if (source == null) {
            log.debug("源对象为null，返回默认值: {}", defaultValue);
            return defaultValue;
        }

        // 如果已经是目标类型，直接返回
        if (targetType.isInstance(source)) {
            log.debug("源对象已经是目标类型，直接返回: {}", source);
            return (T) source;
        }

        try {
            // 特殊处理Duration和Period类型
            if (targetType == Duration.class) {
                Duration duration = convertToDuration(source, context);
                if (duration == null) {
                    return defaultValue;
                }
                return (T) duration;
            }

            if (targetType == Period.class) {
                Period period = convertToPeriod(source, context);
                if (period == null) {
                    return defaultValue;
                }
                return (T) period;
            }

            // 转换为时间对象
            TemporalAccessor temporal = convertToTemporal(source, context);
            if (temporal == null) {
                log.debug("转换为时间对象失败，返回默认值: {}", defaultValue);
                return defaultValue;
            }

            // 根据目标类型进行转换
            T result = convertTemporalToTargetType(temporal, targetType, defaultValue, context);
            log.debug("时间类型转换完成: {} -> {}", source, result);
            return result;
        } catch (Exception e) {
            log.error("时间类型转换失败: {} -> {}", source, targetType, e);
            throw new ConversionException(ConversionError.CONVERSION_FAILED,
                    "时间类型转换失败: " + source + " -> " + targetType, e);
        }
    }

    /**
     * 将对象转换为时间对象
     *
     * @param source  源对象
     * @param context 转换上下文
     * @return 时间对象
     * @throws ConversionException 转换失败时抛出
     */
    protected TemporalAccessor convertToTemporal(Object source, ConversionContext context) throws ConversionException {
        if (source == null) {
            return null;
        }

        // 如果已经是TemporalAccessor类型，直接返回
        if (source instanceof TemporalAccessor) {
            return (TemporalAccessor) source;
        }

        // 字符串转换
        if (source instanceof String) {
            return convertFromString((String) source, context);
        }

        // 数字转换（时间戳）
        if (source instanceof Number) {
            return convertFromNumber((Number) source, context);
        }

        // Date转换
        if (source instanceof Date) {
            return convertFromDate((Date) source, context);
        }

        // 布尔值转换
        if (source instanceof Boolean) {
            return convertFromBoolean((Boolean) source, context);
        }

        // 字符转换
        if (source instanceof Character) {
            return convertFromCharacter((Character) source, context);
        }

        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "无法将类型 " + source.getClass().getSimpleName() + " 转换为时间类型");
    }

    /**
     * 从字符串转换为时间对象
     */
    private TemporalAccessor convertFromString(String source, ConversionContext context) throws ConversionException {
        String str = source.trim();
        if (str.isEmpty()) {
            return null;
        }

        // Period和Duration特殊处理
        if (str.startsWith("P")) {
            return null;
        }

        // 尝试纯数字（时间戳）
        try {
            long timestamp = Long.parseLong(str);
            return Instant.ofEpochMilli(timestamp);
        } catch (NumberFormatException e) {
            // 继续尝试日期格式
        }

        // 尝试各种日期时间格式（按优先级）
        TemporalAccessor result = tryParseWithFormats(str, DateFormats.TIMEZONE_FORMAT_PATTERNS);
        if (result != null)
            return result;

        result = tryParseWithFormats(str, DateFormats.DATETIME_FORMAT_PATTERNS);
        if (result != null)
            return result;

        result = tryParseWithFormats(str, DateFormats.DATE_FORMAT_PATTERNS);
        if (result != null)
            return result;

        result = tryParseWithFormats(str, DateFormats.TIME_FORMAT_PATTERNS);
        if (result != null)
            return result;

        throw new ConversionException(ConversionError.FORMAT_ERROR,
                "无法将字符串 '" + str + "' 转换为时间类型");
    }

    /**
     * 尝试使用多个格式解析字符串
     */
    private TemporalAccessor tryParseWithFormats(String str, String[] patterns) {
        for (String pattern : patterns) {
            try {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);

                // 根据格式类型选择解析方法
                if (pattern.contains("Z") || pattern.contains("XXX")) {
                    return ZonedDateTime.parse(str, formatter);
                } else if (pattern.contains("H") || pattern.contains("h")) {
                    return LocalDateTime.parse(str, formatter);
                } else if (pattern.contains("y")) {
                    return LocalDate.parse(str, formatter);
                } else {
                    return LocalTime.parse(str, formatter);
                }
            } catch (DateTimeParseException e) {
                // 尝试下一个格式
            }
        }
        return null;
    }

    /**
     * 从数字转换为时间对象（时间戳）
     */
    private TemporalAccessor convertFromNumber(Number source, ConversionContext context) {
        long milliseconds = source.longValue();
        return Instant.ofEpochMilli(milliseconds);
    }

    /**
     * 从Date转换为时间对象
     */
    private TemporalAccessor convertFromDate(Date source, ConversionContext context) {
        return source.toInstant();
    }

    /**
     * 从布尔值转换为时间对象
     */
    private TemporalAccessor convertFromBoolean(Boolean source, ConversionContext context) {
        return source ? Instant.EPOCH : Instant.EPOCH.plusSeconds(1);
    }

    /**
     * 从字符转换为时间对象
     */
    private TemporalAccessor convertFromCharacter(Character source, ConversionContext context) {
        return Instant.EPOCH.plusSeconds(source.charValue());
    }

    /**
     * 将时间对象转换为目标类型
     *
     * @param <T>          目标类型
     * @param temporal     源时间对象
     * @param targetType   目标类型
     * @param defaultValue 默认值
     * @param context      转换上下文
     * @return 转换结果
     * @throws ConversionException 转换失败时抛出
     */
    @SuppressWarnings("unchecked")
    protected <T> T convertTemporalToTargetType(TemporalAccessor temporal, Class<T> targetType, T defaultValue,
            ConversionContext context)
            throws ConversionException {

        String typeName = targetType.getSimpleName();

        try {
            return switch (typeName) {
                case "LocalDate" -> (T) convertToLocalDate(temporal, context);
                case "LocalTime" -> (T) convertToLocalTime(temporal, context);
                case "LocalDateTime" -> (T) convertToLocalDateTime(temporal, context);
                case "ZonedDateTime" -> (T) convertToZonedDateTime(temporal, context);
                case "Instant" -> (T) convertToInstant(temporal, context);
                case "Year" -> (T) convertToYear(temporal, context);
                case "YearMonth" -> (T) convertToYearMonth(temporal, context);
                // Period和Duration在上层已经处理，这里不应该到达
                case "Period" -> null;
                case "Duration" -> null;
                case "Date" -> (T) convertToDate(temporal, context);
                case "Temporal" -> (T) (temporal instanceof Temporal ? temporal : null);
                case "TemporalAccessor" -> (T) temporal;
                default -> throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                        "不支持的时间类型: " + targetType);
            };
        } catch (Exception e) {
            throw new ConversionException(ConversionError.CONVERSION_FAILED,
                    "时间类型转换失败: " + temporal + " -> " + targetType, e);
        }
    }

    /**
     * 转换为LocalDate
     */
    protected LocalDate convertToLocalDate(TemporalAccessor temporal, ConversionContext context) {
        if (temporal instanceof LocalDate ld)
            return ld;
        if (temporal instanceof LocalDateTime ldt)
            return ldt.toLocalDate();
        if (temporal instanceof ZonedDateTime zdt)
            return zdt.toLocalDate();
        if (temporal instanceof Instant instant)
            return instant.atZone(DEFAULT_ZONE_ID).toLocalDate();
        if (temporal instanceof Year year)
            return year.atDay(1);
        if (temporal instanceof YearMonth yearMonth)
            return yearMonth.atDay(1);

        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "无法将 " + temporal.getClass().getSimpleName() + " 转换为LocalDate");
    }

    /**
     * 转换为LocalTime
     */
    protected LocalTime convertToLocalTime(TemporalAccessor temporal, ConversionContext context) {
        if (temporal instanceof LocalTime lt)
            return lt;
        if (temporal instanceof LocalDateTime ldt)
            return ldt.toLocalTime();
        if (temporal instanceof ZonedDateTime zdt)
            return zdt.toLocalTime();
        if (temporal instanceof Instant instant)
            return instant.atZone(DEFAULT_ZONE_ID).toLocalTime();

        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "无法将 " + temporal.getClass().getSimpleName() + " 转换为LocalTime");
    }

    /**
     * 转换为LocalDateTime
     */
    protected LocalDateTime convertToLocalDateTime(TemporalAccessor temporal, ConversionContext context) {
        if (temporal instanceof LocalDateTime ldt)
            return ldt;
        if (temporal instanceof LocalDate ld)
            return ld.atStartOfDay();
        if (temporal instanceof LocalTime lt)
            return LocalDate.now().atTime(lt);
        if (temporal instanceof ZonedDateTime zdt)
            return zdt.toLocalDateTime();
        if (temporal instanceof Instant instant)
            return instant.atZone(DEFAULT_ZONE_ID).toLocalDateTime();
        if (temporal instanceof Year year)
            return year.atDay(1).atStartOfDay();
        if (temporal instanceof YearMonth yearMonth)
            return yearMonth.atDay(1).atStartOfDay();

        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "无法将 " + temporal.getClass().getSimpleName() + " 转换为LocalDateTime");
    }

    /**
     * 转换为ZonedDateTime
     */
    protected ZonedDateTime convertToZonedDateTime(TemporalAccessor temporal, ConversionContext context) {
        if (temporal instanceof ZonedDateTime zdt)
            return zdt;
        if (temporal instanceof LocalDateTime ldt)
            return ldt.atZone(DEFAULT_ZONE_ID);
        if (temporal instanceof LocalDate ld)
            return ld.atStartOfDay(DEFAULT_ZONE_ID);
        if (temporal instanceof LocalTime lt)
            return LocalDate.now().atTime(lt).atZone(DEFAULT_ZONE_ID);
        if (temporal instanceof Instant instant)
            return instant.atZone(DEFAULT_ZONE_ID);

        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "无法将 " + temporal.getClass().getSimpleName() + " 转换为ZonedDateTime");
    }

    /**
     * 转换为Instant
     */
    protected Instant convertToInstant(TemporalAccessor temporal, ConversionContext context) {
        if (temporal instanceof Instant instant)
            return instant;
        if (temporal instanceof LocalDateTime ldt)
            return ldt.atZone(DEFAULT_ZONE_ID).toInstant();
        if (temporal instanceof LocalDate ld)
            return ld.atStartOfDay(DEFAULT_ZONE_ID).toInstant();
        if (temporal instanceof LocalTime lt)
            return LocalDate.now().atTime(lt).atZone(DEFAULT_ZONE_ID).toInstant();
        if (temporal instanceof ZonedDateTime zdt)
            return zdt.toInstant();

        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "无法将 " + temporal.getClass().getSimpleName() + " 转换为Instant");
    }

    /**
     * 转换为Year
     */
    protected Year convertToYear(TemporalAccessor temporal, ConversionContext context) {
        if (temporal instanceof Year year)
            return year;
        if (temporal instanceof YearMonth yearMonth)
            return Year.from(yearMonth);
        if (temporal instanceof LocalDate ld)
            return Year.from(ld);
        if (temporal instanceof LocalDateTime ldt)
            return Year.from(ldt);
        if (temporal instanceof ZonedDateTime zdt)
            return Year.from(zdt);

        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "无法将 " + temporal.getClass().getSimpleName() + " 转换为Year");
    }

    /**
     * 转换为YearMonth
     */
    protected YearMonth convertToYearMonth(TemporalAccessor temporal, ConversionContext context) {
        if (temporal instanceof YearMonth yearMonth)
            return yearMonth;
        if (temporal instanceof Year year)
            return year.atMonth(1);
        if (temporal instanceof LocalDate ld)
            return YearMonth.from(ld);
        if (temporal instanceof LocalDateTime ldt)
            return YearMonth.from(ldt);
        if (temporal instanceof ZonedDateTime zdt)
            return YearMonth.from(zdt);

        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "无法将 " + temporal.getClass().getSimpleName() + " 转换为YearMonth");
    }

    /**
     * 转换为Period
     */
    protected Period convertToPeriod(Object source, ConversionContext context) {
        if (source instanceof Period) {
            return (Period) source;
        } else if (source instanceof String) {
            String str = ((String) source).trim();
            try {
                return Period.parse(str);
            } catch (Exception e) {
                throw new ConversionException(ConversionError.FORMAT_ERROR,
                        "无法将字符串 '" + str + "' 转换为Period", e);
            }
        } else if (source instanceof Number) {
            // 数字转换为天数
            return Period.ofDays(((Number) source).intValue());
        }
        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "无法将 " + source.getClass().getSimpleName() + " 转换为Period");
    }

    /**
     * 转换为Duration
     */
    protected Duration convertToDuration(Object source, ConversionContext context) {
        if (source instanceof Duration) {
            return (Duration) source;
        } else if (source instanceof String) {
            String str = ((String) source).trim();
            try {
                return Duration.parse(str);
            } catch (Exception e) {
                throw new ConversionException(ConversionError.FORMAT_ERROR,
                        "无法将字符串 '" + str + "' 转换为Duration", e);
            }
        } else if (source instanceof Number) {
            // 数字转换为毫秒
            return Duration.ofMillis(((Number) source).longValue());
        }
        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "无法将 " + source.getClass().getSimpleName() + " 转换为Duration");
    }

    /**
     * 转换为Date
     */
    protected Date convertToDate(TemporalAccessor temporal, ConversionContext context) {
        Instant instant;

        if (temporal instanceof Instant i) {
            instant = i;
        } else if (temporal instanceof LocalDateTime ldt) {
            instant = ldt.atZone(DEFAULT_ZONE_ID).toInstant();
        } else if (temporal instanceof LocalDate ld) {
            instant = ld.atStartOfDay(DEFAULT_ZONE_ID).toInstant();
        } else if (temporal instanceof LocalTime lt) {
            instant = LocalDate.now().atTime(lt).atZone(DEFAULT_ZONE_ID).toInstant();
        } else if (temporal instanceof ZonedDateTime zdt) {
            instant = zdt.toInstant();
        } else {
            throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                    "无法将 " + temporal.getClass().getSimpleName() + " 转换为Date");
        }

        return Date.from(instant);
    }

    @Override
    public boolean supports(Class<?> targetType) {
        // 支持所有时间类型
        return TemporalAccessor.class.isAssignableFrom(targetType) ||
                targetType == Date.class ||
                targetType == LocalDate.class || targetType == LocalTime.class ||
                targetType == LocalDateTime.class || targetType == ZonedDateTime.class ||
                targetType == Instant.class || targetType == Year.class ||
                targetType == YearMonth.class || targetType == Period.class ||
                targetType == Duration.class;
    }

    @Override
    public Class<?> getSupportedType() {
        return TemporalAccessor.class;
    }

    @Override
    public String getName() {
        return "DateTimeConverter";
    }

    /**
     * 安全的时间类型转换
     *
     * @param <T>        目标类型
     * @param value      源时间对象
     * @param targetType 目标类型
     * @return 转换结果
     * @throws ConversionException 转换失败时抛出
     */
    @SuppressWarnings("unchecked")
    public static <T> T convertSafely(TemporalAccessor value, Class<T> targetType) throws ConversionException {
        if (value == null) {
            return null;
        }

        if (targetType.isInstance(value)) {
            return (T) value;
        }

        // 这里可以实现更复杂的转换逻辑
        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "不支持的时间类型转换: " + value.getClass().getSimpleName() + " -> " + targetType.getSimpleName());
    }

    /**
     * 检查时间类型
     *
     * @param clazz 类型
     * @return 是否为时间类型
     */
    public static boolean isDateTimeType(Class<?> clazz) {
        return TemporalAccessor.class.isAssignableFrom(clazz) ||
                clazz == Date.class ||
                clazz == LocalDate.class || clazz == LocalTime.class ||
                clazz == LocalDateTime.class || clazz == ZonedDateTime.class ||
                clazz == Instant.class || clazz == Year.class ||
                clazz == YearMonth.class || clazz == Period.class ||
                clazz == Duration.class;
    }

    /**
     * 获取时间类型的默认值
     *
     * @param <T>   时间类型
     * @param clazz 类型
     * @return 默认值
     */
    @SuppressWarnings("unchecked")
    public static <T> T getDefaultValue(Class<T> clazz) {
        String typeName = clazz.getSimpleName();

        return switch (typeName) {
            case "LocalDate" -> (T) LocalDate.now();
            case "LocalTime" -> (T) LocalTime.now();
            case "LocalDateTime" -> (T) LocalDateTime.now();
            case "ZonedDateTime" -> (T) ZonedDateTime.now();
            case "Instant" -> (T) Instant.now();
            case "Year" -> (T) Year.now();
            case "YearMonth" -> (T) YearMonth.now();
            case "Period" -> (T) Period.ZERO;
            case "Duration" -> (T) Duration.ZERO;
            case "Date" -> (T) new Date();
            default -> null;
        };
    }
}
